#ifndef CGRAVITATIONAL_ACRETION_H
#define CGRAVITATIONAL_ACRETION_H

#include <vector>
#include <iostream>
#include <time.h>
#include <stdio.h>
using namespace std;

template <typename _SNO> class CGravitationalAcretion {
public:
	typedef typename _SNO::particle_t		particle_t;
	typedef typename _SNO::value_point_t	value_point_t;
	typedef typename _SNO::value_pattern_t	value_pattern_t;
	typedef typename _SNO::gravity_t		gravity_t;
	typedef typename _SNO::velocity_t		velocity_t;
	typedef typename _SNO::container_t		container_t;
	typedef typename _SNO::label_t			label_t;
	typedef typename _SNO::pattern_t		pattern_t;

	typedef vector<particle_t*>				planetesimal_t;

public:
	planetesimal_t	_planetesimals;

	CGravitationalAcretion (particle_t	***& _nebula, int _size, double _gf, double _vf, int _t, int _me, double _mean_error){
		int _limit = (int)ceil(sqrt((double)_size));
		srand((unsigned int)time(NULL));
		cout << "Planetesimals Selection" << endl;
		while ((int)_planetesimals.size() < _limit){
			unsigned int x = rand () % _limit;
			unsigned int y = rand () % _limit;
			while (_nebula[x][y] == NULL){
				x = rand () % _limit;
				y = rand () % _limit;
			}
			if (_nebula[x][y]->_is_planetesimal == false){
				_nebula[x][y]->_container.push_back(_nebula[x][y]->_pattern);
				_planetesimals.push_back(_nebula[x][y]);
				_nebula[x][y]->_is_planetesimal =  true;
			}
		}
	
		cout << "Gravitational Acretion" << endl;
		while (_t--) {
			cout << "Iteracion: " << _t << endl;
			for (int i = 0; i < _limit; i++){
				_planetesimals[i]->acretion(_nebula, _limit, _gf, _vf, _mean_error);
			}
			__photoevaphoration (_nebula,_limit,_me);
//			system("pause");
		}
	}

	~CGravitationalAcretion (void){
	}

	void __photoevaphoration (particle_t ***& _nebula, int _l, int _me){
		for (int i = 0; i < _l; i++)
			for (int k = 0; k < _l; k++)
				if (_nebula[i][k])
					if ((int)_nebula[i][k]->_container.size()  < _me) {
						int x = rand () % _l;
						int y = rand () % _l;
						while (_nebula[x][y]){
							x = rand () % _l;
							y = rand () % _l;
						}
						swap(_nebula[i][k],_nebula[x][y]);
						_nebula[x][y]->_point_in_plane[X] = x;
						_nebula[x][y]->_point_in_plane[Y] = y;
					}
	}

	template <typename __SNO>
	friend ostream& operator<< (ostream& out, const CGravitationalAcretion<__SNO>& _g) {
		int size = 0;
		for (int i = 0; i < (int)_g._planetesimals.size(); i++){
			out << (*_g._planetesimals[i]) << endl;

			vector<string> _labels;
			vector<int> _counters;

			container_t::iterator it = _g._planetesimals[i]->_container.begin();
			for (; it != _g._planetesimals[i]->_container.end(); it++){
				
				bool exist = false;
				int k = 0;
				for (; k < _labels.size(); k++)
					if (_labels[k] == it->_label) {exist =  true; break;}

				if (exist) _counters[k] ++; 
				else {
					_labels.push_back(it->_label);
					_counters.push_back(1);
				}

//				out << it->_label << endl;
			}
			for (int k = 0; k < _labels.size(); k++)
				out << _labels[k] << " : " << _counters[k] << endl;
			size += _g._planetesimals[i]->_container.size();
		}
		out << "Total of elements: " << size << endl;


		return out;
	}

};

#endif